home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 11
/
CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso
/
cucd
/
programming
/
oberonv4
/
source
/
system
/
locelems.mod
(
.txt
)
< prev
next >
Wrap
Oberon Text
|
1996-04-28
|
17KB
|
412 lines
Syntax10.Scn.Fnt
Syntax10i.Scn.Fnt
StampElems
Alloc
28 Apr 96
Syntax10b.Scn.Fnt
FoldElems
MODULE LocElems; (** SHML, 4 Jan 96,
, based on PopupElems **)
(** Provide menu to locate positions in a text. As default, it searches for procedure headings.
Other search procedures for specific file extensions can be installed. *)
IMPORT
Oberon, Input, Display, Viewers, Files, Fonts, Printer,
Texts, TextFrames, MenuViewers, TextPrinter, Pictures, Amiga;
CONST
Ceres = FALSE;
VersionTag = 0X;
MenuDW = 3; MenuDH = 1; (* margins of menu box *)
DUnit = TextFrames.Unit; PUnit = TextPrinter.Unit;
MR = 0; MM = 1; ML = 2; cancel = {ML, MM, MR};
white = Display.white; black = Display.black;
paint = Display.paint; replace = Display.replace; invert = Display.invert;
StrLen*= 64;
TableLen = 128;
TYPE
String = ARRAY StrLen OF CHAR;
Entry = RECORD
str: String;
pos: LONGINT
END;
Table = ARRAY TableLen OF Entry;
Elem*= POINTER TO ElemDesc;
ElemDesc = RECORD (Texts.ElemDesc)
name: ARRAY 32 OF CHAR;
n, width: INTEGER; (* number of items, width *)
line: BOOLEAN;
stampLen: LONGINT;
t: Table
END;
SearchProc*= PROCEDURE(e: Elem; t: Texts.Text; VAR sort(*out*): BOOLEAN);
Element = POINTER TO ElementDesc;
ElementDesc = RECORD
ext: ARRAY 32 OF CHAR; search: SearchProc;
next: Element
END;
VAR wr: Texts.Writer; buf: Texts.Buffer; root: Element; defaultSearch: SearchProc; saveArea:Pictures.Picture;
PROCEDURE Str(s: ARRAY OF CHAR); BEGIN Texts.WriteString(wr, s) END Str;
PROCEDURE Ln; BEGIN Texts.WriteLn(wr) END Ln;
(* non_portable stuff *)
PROCEDURE Save(X, Y, W, H: INTEGER); (* copy from screen X, Y, W, H into save area *)
BEGIN
Pictures.Create(saveArea,W,H,Amiga.OberonDepth);
Pictures.CopyBlock(Display.screen,saveArea,X,Y,W,H,0,0,replace)
END Save;
PROCEDURE Restore(X, Y, W, H: INTEGER); (* restore from save area to screen X, Y, W, H *)
BEGIN
Pictures.CopyBlock(saveArea,Display.screen,0,0,W,H,X,Y,replace)
END Restore;
(* auxiliary *)
PROCEDURE Min(x, y: INTEGER): INTEGER; BEGIN IF x < y THEN RETURN x ELSE RETURN y END END Min;
PROCEDURE Max(x, y: INTEGER): INTEGER; BEGIN IF x > y THEN RETURN x ELSE RETURN y END END Max;
PROCEDURE StrDispWidth(fnt: Fonts.Font; s: ARRAY OF CHAR): LONGINT;
VAR pat: Display.Pattern; width, i, dx, x, y, w, h: INTEGER;
BEGIN
width := 0; i := 0;
WHILE s[i] # 0X DO Display.GetChar(fnt.raster, s[i], dx, x, y, w, h, pat); INC(width, dx); INC(i) END;
RETURN LONG(width)*DUnit
END StrDispWidth;
PROCEDURE DispStr(fnt: Fonts.Font; s: ARRAY OF CHAR; col, x0, y0: INTEGER);
VAR pat: Display.Pattern; i, dx, x, y, w, h: INTEGER;
BEGIN
i := 0;
WHILE s[i] # 0X DO
Display.GetChar(fnt.raster, s[i], dx, x, y, w, h, pat);
Display.CopyPattern(col, pat, x0+x, y0+y, paint);
INC(i); INC(x0, dx)
END
END DispStr;
(* change propagation *)
PROCEDURE PrepareDraw(e: Elem; fnt: Fonts.Font; VAR dy: INTEGER);
VAR width, dh: INTEGER;
BEGIN
width := 0; dh := 0; dy := fnt.minY;
IF dy > -2 THEN dy := -2 END;
e.W := LONG(width)*DUnit+StrDispWidth(fnt, e.name)+DUnit; e.H := LONG(fnt.maxY-fnt.minY+dh)*DUnit
END PrepareDraw;
PROCEDURE Sort(e: Elem);
(* sort the array with insertion sort (because it's stable!) *)
VAR i, j: INTEGER; entry: Entry;
BEGIN
FOR j := 1 TO e.n-1 DO
entry := e.t[j];
i := j-1;
WHILE (i >= 0) & (entry.str < e.t[i].str) DO e.t[i+1] := e.t[i]; DEC(i) END;
e.t[i+1] := entry
END
END Sort;
PROCEDURE Append*(e: Elem; str: ARRAY OF CHAR; pos: LONGINT): BOOLEAN;
(** append str and pos to table in element e, return "table is full"; (LEN(str) <= StrLen, 100) *)
BEGIN
ASSERT(LEN(str) <= StrLen, 100);
IF e.n < TableLen THEN COPY(str, e.t[e.n].str); e.t[e.n].pos := pos; INC(e.n) END;
RETURN e.n = TableLen
END Append;
PROCEDURE DefaultSearch(e: Elem; t: Texts.Text; VAR sort(*out*): BOOLEAN);
VAR s: Texts.Scanner; str, type: ARRAY 32 OF CHAR; class, i, j: INTEGER;
BEGIN
Texts.OpenScanner(s, t, 0);
LOOP
WHILE ~s.eot & ((s.class # Texts.Name) OR (s.s # "PROCEDURE")) DO Texts.Scan(s) END;
IF s.eot THEN EXIT END;
(* s.s = PROCEDURE *)
type := "";
Texts.Scan(s);
IF ~((s.class = Texts.Char) & (s.c = "^")) THEN (* ignore forward declarations *)
IF s.class = Texts.Char THEN (* ( *)
IF s.c = "(" THEN
REPEAT COPY(s.s, type); class := s.class; Texts.Scan(s)
UNTIL s.eot OR (class = Texts.Name) & (s.class = Texts.Char) & (s.c = ")");
IF s.eot THEN EXIT END
END;
Texts.Scan(s)
END;
IF s.class = Texts.Name THEN
i := -1;
IF type # "" THEN
REPEAT INC(i); str[i] := type[i] UNTIL str[i] = 0X;
str[i] := "."
END;
j := -1;
REPEAT INC(j); INC(i); str[i] := s.s[j] UNTIL str[i] = 0X;
IF Append(e, str, Texts.Pos(s)-1) THEN EXIT END
END
END
END;
sort := TRUE
END DefaultSearch;
PROCEDURE Extension(name: ARRAY OF CHAR; VAR ext: ARRAY OF CHAR);
VAR i, j: INTEGER;
BEGIN
i := -1; REPEAT INC(i) UNTIL name[i] = 0X;
REPEAT DEC(i) UNTIL (name[i] = ".") OR (i = 0);
IF i = 0 THEN ext[0] := 0X
ELSE
j := -1; REPEAT INC(i); INC(j); ext[j] := name[i] UNTIL (name[i] = 0X) OR (name[i] = '"');
ext[j] := 0X
END
END Extension;
PROCEDURE Search(ext: ARRAY OF CHAR; VAR prev: Element): Element;
VAR l: Element;
BEGIN
l := root; prev := NIL;
WHILE (l # NIL) & (l.ext # ext) DO prev := l; l := l.next END;
RETURN l
END Search;
PROCEDURE Refresh(e: Elem; t: Texts.Text; menuFrame: Display.Frame); (* generate menu text from t *)
VAR
s: Texts.Scanner; ext: ARRAY 32 OF CHAR; this, prev: Element;
i, j, width, n, dx, x, y, w, h: INTEGER; p: LONGINT; sort: BOOLEAN;
BEGIN
IF t # NIL THEN
e.n := 0; e.stampLen := t.len; sort := FALSE;
WITH menuFrame: TextFrames.Frame DO
Texts.OpenScanner(s, menuFrame.text, 0); Texts.Scan(s);
IF s.class IN {Texts.Name, Texts.String} THEN
Extension(s.s, ext); this := Search(ext, prev);
IF this # NIL THEN this.search(e, t, sort) ELSE defaultSearch(e, t, sort) END
ELSE defaultSearch(e, t, sort)
END
ELSE defaultSearch(e, t, sort)
END;
IF e.n > 0 THEN
IF sort THEN Sort(e) END
ELSE e.t[0].str := "no items in text"; e.t[0].pos := -1; e.n := 1
END;
n := e.n;
WHILE n*Fonts.Default.height + 2*MenuDH + 4 > Oberon.DisplayHeight(0) DO DEC(n) END;
IF n < e.n THEN
e.n := n;
Str("too many procedures, not all will be shown!"); Ln;
Texts.Append(Oberon.Log, wr.buf)
END;
e.width := 0;
FOR i := 0 TO n-1 DO
j := 0; width := 0;
WHILE e.t[i].str[j] # 0X DO
Display.GetChar(Fonts.Default.raster, e.t[i].str[j], dx, x, y, w, h, p); INC(width, dx);
INC(j)
END;
e.width := Max(e.width, width)
END
ELSE e.n := 0
END
END Refresh;
(* file input/output *)
PROCEDURE Load(VAR r: Files.Rider; e: Elem);
VAR ch: CHAR;
BEGIN
Files.Read(r, ch);
IF ch = VersionTag THEN Files.ReadString(r, e.name); Files.ReadBool(r, e.line) END
END Load;
PROCEDURE Store(VAR r: Files.Rider; e: Elem);
BEGIN Files.Write(r, VersionTag); Files.WriteString(r, e.name); Files.WriteBool(r, e.line)
END Store;
(* graphics *)
PROCEDURE Box(col, bkgnd, X, Y, W, H: INTEGER);
BEGIN
Display.ReplConst(col, X+1, Y+1, W-2, 1, replace);
Display.ReplConst(col, X+1, Y+H-2, W-2, 1, replace);
Display.ReplConst(col, X+1, Y+2, 1, H-4, replace);
Display.ReplConst(col, X+W-2, Y+2, 1, H-4, replace);
Display.ReplConst(col, X+4, Y, W-4, 1, replace);
Display.ReplConst(col, X+W-1, Y+1, 1, H-4, replace);
Display.ReplConst(bkgnd, X+2, Y+2, W-4, H-4, replace)
END Box;
PROCEDURE DrawElem(e: Elem; f: Display.Frame; pos: LONGINT; fnt: Fonts.Font; col, X, Y: INTEGER);
VAR beg: LONGINT; parc: TextFrames.Parc; bkgndCol: INTEGER;
BEGIN
IF f IS TextFrames.Frame THEN bkgndCol := f(TextFrames.Frame).col ELSE bkgndCol := black END;
TextFrames.ParcBefore(Texts.ElemBase(e), pos, parc, beg);
INC(Y, SHORT(parc.dsr DIV DUnit));
IF bkgndCol = col THEN col := ABS(white-col) END;
DispStr(fnt, e.name, col, X, Y);
IF e.line THEN Display.ReplPatternC(f, white, Display.grey1, X, Y-2, SHORT(e.W DIV DUnit), 1, X, Y-1, invert) END
END DrawElem;
PROCEDURE PrintElem(e: Elem; fnt: Fonts.Font; X, Y: INTEGER);
BEGIN
Printer.String(X, Y, e.name, fnt.name);
IF e.line THEN Printer.ReplConst(X, Y-2, SHORT((e.W-1) DIV PUnit), 1) END
END PrintElem;
PROCEDURE DrawMenu(e: Elem; X, Y, W, H: INTEGER);
VAR X0, dx, x, y, w, h, i, j: INTEGER; p: LONGINT;
BEGIN
Box(white, black, X, Y, W, H);
X0 := X+MenuDW+2; Y := Y+H-Fonts.Default.height-Fonts.Default.minY-MenuDH-2;
FOR i := 0 TO e.n-1 DO
j := 0; X := X0;
WHILE e.t[i].str[j] # 0X DO
Display.GetChar(Fonts.Default.raster, e.t[i].str[j], dx, x, y, w, h, p);
Display.CopyPattern(Display.white, p, X+x, Y+y, paint); INC(X, dx);
INC(j)
END;
DEC(Y, Fonts.Default.height)
END
END DrawMenu;
(* actions *)
PROCEDURE Show(e: Elem; X, Y: INTEGER; VAR cmd: INTEGER; VAR keySum: SET);
VAR
eH, W, H, w, newY, mx, my, top, bot, left, right, newCmd: INTEGER;
keys: SET;
PROCEDURE Flip(cmd: INTEGER);
BEGIN
IF cmd >= 0 THEN
Display.ReplConst(white, left, top-(cmd+1)*Fonts.Default.height, right-left, Fonts.Default.height, invert)
END
END Flip;
BEGIN
eH := SHORT(e.H DIV DUnit);
Input.Mouse(keys, mx, my);
W := e.width + 2*MenuDW + 4; H := e.n*Fonts.Default.height + 2*MenuDH + 4;
IF (e.n = 0) OR (W > Oberon.DisplayWidth(X)) OR (H > Oberon.DisplayHeight(X)) THEN
IF e.n > 0 THEN Str("LocElem too big!"); Ln; Texts.Append(Oberon.Log, wr.buf) END;
REPEAT Input.Mouse(keys, mx, my); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, mx, my)
UNTIL keys = {};
keySum := cancel; cmd := -1
ELSE
w := Oberon.DisplayWidth(X); left := Display.Left;
IF Ceres & (X >= Display.Left+Display.Width) THEN (* adjust if on secondary *)
INC(w, Display.Width); left := Display.Left+Display.Width
END;
X := Min(w-W, Max(mx-W DIV 2, left)); (* X >= left & X+W <= w *)
newY := my-((e.n-cmd)*Fonts.Default.height-Fonts.Default.height DIV 2);
IF (newY >= Display.Bottom) & (newY+H <= Oberon.DisplayHeight(X)) THEN (* popup at mouse pos *)
Y := newY
ELSE (* drop down *)
IF Y-H > Display.Bottom THEN Y := Y-H ELSE Y := Y+eH END;
IF Y+H > Oberon.DisplayHeight(X) THEN Y := Display.Bottom END
END;
left := X+3; right := X+W-3; bot := Y+MenuDH+3; top := Y+H-MenuDH-2;
Oberon.RemoveMarks(X, Y, W, H); Oberon.FadeCursor(Oberon.Mouse);
Save(X, Y, W, H); (* save background *)
DrawMenu(e, X, Y, W, H);
Flip(cmd); keySum := {};
REPEAT
Input.Mouse(keys, mx, my); keySum := keySum+keys;
Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, mx, my);
IF keySum = cancel THEN cmd := -1
ELSIF (mx >= left) & (mx <= right) & (my >= bot) & (my <= top) THEN
newCmd := (top-my) DIV Fonts.Default.height;
IF newCmd # cmd THEN Flip(cmd); Flip(newCmd); cmd := newCmd END
ELSE Flip(cmd); cmd := -1
END
UNTIL keys = {};
Oberon.FadeCursor(Oberon.Mouse);
Restore(X, Y, W, H) (* restore background *)
END
END Show;
PROCEDURE Popup(e: Elem; msg: TextFrames.TrackMsg);
VAR
v: Viewers.Viewer; tf: TextFrames.Frame;
cmd: INTEGER; keys: SET;
beg, end: LONGINT;
BEGIN
v := Viewers.This(msg.frame.X, msg.frame.Y);
IF (v IS MenuViewers.Viewer) & (v.dsc = msg.frame) & (v.dsc.next IS TextFrames.Frame) THEN
tf := v.dsc.next(TextFrames.Frame);
IF tf.text.len # e.stampLen THEN Refresh(e, tf.text, msg.frame) END;
keys := msg.keys; cmd := 0;
Show(e, msg.X0, msg.Y0, cmd, keys);
IF keys = {MM, MR} THEN Refresh(e, tf.text, msg.frame)
ELSIF (keys # cancel) & (cmd > -1) & (e.t[cmd].pos >= 0) THEN
beg := tf.org; end := TextFrames.Pos(tf, tf.X+tf.W, tf.Y);
IF (e.t[cmd].pos < beg) OR (end <= e.t[cmd].pos) THEN TextFrames.Show(tf, e.t[cmd].pos) END;
Oberon.PassFocus(v);
TextFrames.SetCaret(tf, e.t[cmd].pos)
END
ELSE Str("LocElem not in menu viewer or content frame is not TextFrame"); Ln; Texts.Append(Oberon.Log, wr.buf)
END
END Popup;
(* element *)
PROCEDURE Handle(e: Texts.Elem; VAR msg: Texts.ElemMsg);
VAR copy: Elem;
BEGIN
WITH e: Elem DO
IF msg IS TextFrames.DisplayMsg THEN
WITH msg: TextFrames.DisplayMsg DO
IF msg.prepare THEN PrepareDraw(e, msg.fnt, msg.Y0)
ELSE DrawElem(e, msg.frame, msg.pos, msg.fnt, msg.col, msg.X0, msg.Y0)
END
END
ELSIF msg IS TextPrinter.PrintMsg THEN
WITH msg: TextPrinter.PrintMsg DO
IF ~msg.prepare THEN PrintElem(e, msg.fnt, msg.X0, msg.Y0) END
END
ELSIF msg IS Texts.CopyMsg THEN
WITH msg: Texts.CopyMsg DO
NEW(copy); Texts.CopyElem(e, copy);
copy.name := e.name; copy.line := e.line;
msg.e := copy
END
ELSIF msg IS Texts.IdentifyMsg THEN
WITH msg: Texts.IdentifyMsg DO
msg.mod := "LocElems"; msg.proc := "Alloc"
END
ELSIF msg IS Texts.FileMsg THEN
WITH msg: Texts.FileMsg DO
IF msg.id = Texts.load THEN Load(msg.r, e)
ELSIF msg.id = Texts.store THEN Store(msg.r, e)
END
END
ELSIF msg IS TextFrames.TrackMsg THEN
WITH msg: TextFrames.TrackMsg DO Popup(e, msg) END
END
END
END Handle;
PROCEDURE Alloc*;
VAR e: Elem;
BEGIN NEW(e); e.handle := Handle; Texts.new := e
END Alloc;
(** commands **)
PROCEDURE Insert*;
VAR e: Elem; ins: TextFrames.InsertElemMsg; s: Texts.Scanner;
BEGIN
NEW(e); e.line := TRUE;
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF ~(s.class IN {Texts.Name, Texts.String}) OR (s.s[0] = 0X) THEN e.name := "Loc" ELSE COPY(s.s, e.name) END;
e.handle := Handle; ins.e := e; Viewers.Broadcast(ins)
END Insert;
PROCEDURE Rename*;
VAR e: Elem; text: Texts.Text; beg, end, time: LONGINT; r: Texts.Reader; s: Texts.Scanner;
BEGIN
Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN
Texts.OpenReader(r, text, beg); Texts.ReadElem(r);
IF (r.elem # NIL) & (r.elem IS Elem) THEN
e := r.elem(Elem);
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF s.class = Texts.Name THEN
COPY(s.s, e.name); text.notify(text, Texts.replace, Texts.ElemPos(e), Texts.ElemPos(e)+1)
END
END
END
END Rename;
PROCEDURE Toggle*;
VAR e: Elem; text: Texts.Text; beg, end, time: LONGINT; r: Texts.Reader;
BEGIN
Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN
Texts.OpenReader(r, text, beg); Texts.ReadElem(r);
IF (r.elem # NIL) & (r.elem IS Elem) THEN
e := r.elem(Elem); e.line := ~e.line; text.notify(text, Texts.replace, Texts.ElemPos(e), Texts.ElemPos(e)+1)
END
END
END Toggle;
PROCEDURE Install*(ext: ARRAY OF CHAR; search: SearchProc);
VAR new, this, prev: Element;
BEGIN
IF ext = "*" THEN defaultSearch := search
ELSE
NEW(new); COPY(ext, new.ext); new.search := search;
this := Search(new.ext, prev); (* check for duplicates *)
IF this = NIL THEN new.next := root; root := new (* new entry *)
ELSIF this.search # new.search THEN (* new entry for existing extension -> remove this *)
IF this = root THEN new.next := root.next; root := new
ELSE new.next := this.next; prev.next := new
END
END
END
END Install;
BEGIN NEW(buf); Texts.OpenBuf(buf); Texts.OpenWriter(wr); root := NIL; defaultSearch := DefaultSearch; NEW(saveArea)
END LocElems.